﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace JYH.Framework
{
    public class ModbusHelper
    {


    }

    /// <summary>
    /// Modbus Ascii 数据传输
    /// </summary>
    public class ModbusAscii
    {

        private string receiveString;
        SerialPort m_sp;
        int overTime;//接收超时
        object locked;


        #region ModBus类构造函数
        public ModbusAscii(string portName = "COM1", int baudRate = 9600, int dataBits = 8, int stopBits = 1, string parity = "None")    //（串口号"COM1"、波特率"9600"、数据位"7/8"、停止位"0/1"、校验方式"Odd/Even/None"）
        {
            m_sp = new SerialPort();
            m_sp.PortName = portName;
            m_sp.BaudRate = baudRate;
            m_sp.DataBits = dataBits;
            m_sp.StopBits = (System.IO.Ports. StopBits)stopBits;
            m_sp.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(Parity), parity, true);
            m_sp.ReadTimeout = -1;

            locked = new object();
            receiveString = "";
            overTime = 800;

            m_sp.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            open();
        }
        #endregion



        #region 打开串口
        public bool open()
        {
            if (!m_sp.IsOpen) m_sp.Open();
            Thread.Sleep(100);
            return m_sp.IsOpen;
        }
        #endregion



        #region 关闭串口
        public void close()
        {
            if (m_sp.IsOpen) m_sp.Close();
        }
        #endregion



        #region 接收返回数据
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            receiveString += m_sp.ReadExisting();
            if (receiveString.Length > 1000) receiveString = null;
        }
        #endregion



        #region  字符串转化为待发送的字节数组
        private byte[] StrToBytes(string str)
        {
            int len = str.Length;
            char[] ch0 = str.ToCharArray();
            char[] ch1 = { '\0', '\0', '\r', '\n' };
            char[] ch2 = LRC(str).ToCharArray();
            ch2.CopyTo(ch1, 0);

            char[] ch = new char[len + 5];
            ch[0] = ':';
            ch0.CopyTo(ch, 1);
            ch1.CopyTo(ch, len + 1);


            byte[] result = new byte[len + 5];
            for (int i = 0; i < len + 5; i++)
            {
                result[i] = Convert.ToByte(ch[i]);
            }
            return result;
        }
        #endregion



        #region LRC校验
        public string LRC(string str_data)
        {
            int sum = 0;
            string hex = null;
            for (int i = 0; i < str_data.Length - 1; i = i + 2)
            {
                string c_data = str_data.Substring(i, 2);
                sum += Convert.ToInt32(c_data, 16);
            }
            sum = ~sum + 1;
            sum &= 0xFF;
            hex = sum.ToString("X");
            if (hex.Length < 0) hex = "0" + hex;
            return hex;
        }
        #endregion



        #region 串口发送/获取返回数据
        private string SendData(string SendBuf)
        {
            string outString = null;
            if (m_sp.IsOpen)
            {
                Monitor.Enter(locked);
                receiveString = "";

                string sendString = ":" + SendBuf + LRC(SendBuf) + "\r\n";
                m_sp.Write(sendString);

                int startTime = Environment.TickCount;
                Thread.Sleep(50);
                while (true)
                {
                    int indexEnd = receiveString.LastIndexOf("\r\n");
                    if (indexEnd > 0)
                    {
                        int indexCurr = receiveString.LastIndexOf(':');

                        if (indexCurr >= 0 && indexEnd > (indexCurr + 6))
                        {
                            string recStr = receiveString.Substring(indexCurr + 1, indexEnd - indexCurr - 3);
                            string lrc = receiveString.Substring(indexEnd - 2, 2);
                            if (LRC(recStr).Equals(lrc))
                            {
                                string inStr = SendBuf.Substring(0, 4);
                                string outStr = recStr.Substring(0, 4);
                                //站号和功能码一致，则输出
                                if (inStr.Equals(outStr)) outString = recStr;
                            }
                        }
                        break;
                    }
                    int endTime = Environment.TickCount;
                    if (startTime > endTime || (endTime - startTime) > overTime) break;
                }
                Monitor.Exit(locked);
            }
            return outString;
        }
        #endregion



        #region 字节转字符串
        private string ByteToString(byte inByte)
        {
            string outString = inByte.ToString("X");
            if (outString.Length == 1) outString = "0" + outString;
            return outString;
        }
        #endregion



        #region Bool数组转字符串 boolsToString()
        private string BoolsToString(bool[] inBools)
        {
            int len = inBools.Length;
            int byteNum = (len >> 3);
            if ((len & 0x07) > 0) byteNum++;

            int bt = 0, j = 0;
            string outString = "";
            for (int i = 0; i < len; i++)
            {
                if (inBools[i]) bt |= (1 << j);
                j++;
                if (j > 7)
                {
                    outString += ByteToString((byte)bt);
                    j = 0;
                    bt = 0;
                }
            }
            if (j > 0) outString += ByteToString((byte)bt);

            return outString;
        }

        //Bool数组转Byte
        private byte BoolsToByte(bool[] bools)
        {
            if (bools != null && bools.Length > 0)
            {
                byte b = 0;
                int len = bools.Length;
                if (len > 8) len = 8;
                for (int i = 0; i < len; i++)
                {
                    if (bools[i]) b = (Byte)(b | (1 << i));
                }
                return b;
            }
            return 0;
        }
        #endregion



        #region Short转字符串 shortToString()
        private string ShortToString(short inShort)
        {
            string outString = ByteToString((byte)(inShort >> 8));
            outString += ByteToString((byte)(inShort & 0xFF));
            return outString;
        }
        #endregion



        #region 字符串转Short数组
        private short[] StringToShorts(string inString)
        {
            int len = inString.Length;
            while ((len & 7) > 0)
            {
                inString = "0" + inString;
                len++;
            }

            len >>= 3;
            short[] outShorts = new short[len];
            for (int i = 0; i < len; i++)
            {
                outShorts[i] = Convert.ToByte(inString.Substring(i * 4, 4), 16);
            }

            return outShorts;
        }
        #endregion



        #region 字符串转Byte数组
        private byte[] StringToBytes(string inString)
        {
            int len = inString.Length;
            while ((len & 1) == 1)
            {
                inString = "0" + inString;
                len++;
            }

            len >>= 1;
            byte[] outBytes = new byte[len];
            for (int i = 0; i < len; i++)
            {
                outBytes[i] = Convert.ToByte(inString.Substring(i * 2, 2), 16);
            }

            return outBytes;
        }
        #endregion



        #region Byte转Bool数组
        private bool[] ByteToBools(byte inByte)
        {
            Boolean[] array = new Boolean[8];
            for (int i = 0; i < 8; i++)
            { //对于byte的每bit进行判定  
                array[i] = (inByte & 1) == 1;   //判定byte的最后一位是否为1，若为1，则是true；否则是false  
                inByte = (byte)(inByte >> 1);       //将byte右移一位  
            }
            return array;
        }
        #endregion



        #region 字符串转Bool组
        private bool[] StringToBools(string inString)
        {
            byte[] inBytes = StringToBytes(inString);
            int len = inBytes.Length;
            bool[] outBools = new bool[len * 8];
            for (int i = 0; i < len; i++)
            {
                ByteToBools(inBytes[i]).CopyTo(outBools, i * 8);
            }
            return outBools;
        }
        #endregion



        #region 读线圈fun01 0X
        public bool[] fun01(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            string subuffer = ByteToString(Station);    //站号
            subuffer += "01";    //功能码
            subuffer += ShortToString((short)inAddr);
            subuffer += ShortToString((short)inNum);

            string ob = SendData(subuffer);

            if (ob != null)
            {
                byte[] outBytes = StringToBytes(ob);
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (outBytes[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(outBytes[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }
            return outBools;
        }
        #endregion



        #region 读离散量fun02 1X
        public bool[] fun02(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            string subuffer = ByteToString(Station);    //站号
            subuffer += "02";    //功能码
            subuffer += ShortToString((short)inAddr);
            subuffer += ShortToString((short)inNum);

            string ob = SendData(subuffer);

            if (ob != null)
            {
                byte[] outBytes = StringToBytes(ob);
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (outBytes[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(outBytes[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }
            return outBools;
        }
        #endregion



        #region 写单个线圈fun05 0X
        public bool fun05(byte Station, ushort inAddr, bool inBool)
        {
            string subuffer = ByteToString(Station);
            subuffer += "05";
            subuffer += ShortToString((short)inAddr);
            subuffer += inBool ? "FF" : "00";
            subuffer += "00";

            return subuffer.Equals(SendData(subuffer));
        }
        #endregion



        #region 写多个线圈fun0F 0X
        public bool fun0F(byte Station, ushort inAddr, bool[] inBools)
        {
            int len = inBools.Length;
            int byteNum = (len >> 3);
            if ((len & 0x07) > 0) byteNum++;

            string subuffer = ByteToString(Station);
            subuffer += "0F";
            subuffer += ShortToString((short)inAddr);   //起始地址
            subuffer += ShortToString((short)len);    //线圈数量
            subuffer += ByteToString((byte)byteNum);    //字节数
            subuffer += BoolsToString(inBools);    //传送数据

            string recStr = SendData(subuffer);

            return recStr != null;

        }
        #endregion



        #region 读多个寄存器 fun03 4X
        public short[] fun03(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            string subuffer = ByteToString(Station);    //站号
            subuffer += "03";    //功能码
            subuffer += ShortToString((short)inAddr);   //起始地址
            subuffer += ShortToString((short)inNum);   //数据个数

            string recStr = SendData(subuffer);

            if (recStr != null)
            {
                byte[] ob = StringToBytes(recStr);
                if (ob[2] == inNum * 2)
                {
                    outShorts = new short[inNum];
                    for (int i = 0; i < inNum; i++)
                    {
                        outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                    }
                }
            }
            return outShorts;
        }
        #endregion



        #region 读多个寄存器 fun04 3X
        public short[] fun04(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            string subuffer = ByteToString(Station);    //站号
            subuffer += "04";    //功能码
            subuffer += ShortToString((short)inAddr);   //起始地址
            subuffer += ShortToString((short)inNum);   //数据个数

            string recStr = SendData(subuffer);

            if (recStr != null)
            {
                byte[] ob = StringToBytes(recStr);
                if (ob[2] == inNum * 2)
                {
                    outShorts = new short[inNum];
                    for (int i = 0; i < inNum; i++)
                    {
                        outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                    }
                }
            }
            return outShorts;
        }
        #endregion



        #region 写单个寄存器 fun06 4X
        public bool fun06(byte Station, ushort inAddr, short inShort)
        {
            string subuffer = ByteToString(Station);    //站号
            subuffer += "06";    //功能码
            subuffer += ShortToString((short)inAddr);    //起始地址
            subuffer += ShortToString(inShort);    //传送数据

            return subuffer.Equals(SendData(subuffer));
        }
        #endregion



        #region 写多个寄存器fun10 4X
        public bool fun10(byte Station, ushort inAddr, short[] inShorts)
        {
            int len = inShorts.Length;
            byte[] sendByte = new byte[len * 2];

            string subuffer = ByteToString(Station);    //站号
            subuffer += "10";    //功能码
            subuffer += ShortToString((short)inAddr);    //起始地址
            subuffer += ShortToString((short)len);    //数据个数
            subuffer += ByteToString((byte)(len * 2));   //字节长度
            for (int i = 0; i < len; i++)
            {
                subuffer += ShortToString(inShorts[i]);
            }

            string recStr = SendData(subuffer);
            if (recStr == null)
            {
                return false;
            }
            else
            {
                string send_data = subuffer.Substring(4, 8);
                return send_data.Equals(recStr.Substring(4, 8));
            }
        }
        #endregion

    }


    /// <summary>
    /// Modbus Tcp 数据传输
    /// </summary>
    public class ModbusTcp
    {
        IPAddress severIp;
        int severPort;
        Socket clientSocket;
        object locked;


        public ModbusTcp(string ip, int port)
        {
            severIp = IPAddress.Parse(ip);
            severPort = port;
            locked = new object();
        }



        #region 连接服务端
        public bool ConnectToSever()
        {
            if (clientSocket == null || !clientSocket.Connected)
            {
                if (clientSocket != null) clientSocket.Close();
                try
                {
                    clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建Socket
                    clientSocket.Connect(new IPEndPoint(severIp, severPort));//连接到服务器
                    Thread.Sleep(500);
                }
                catch
                {
                    clientSocket.Close();
                }
            }
            return clientSocket != null && clientSocket.Connected;
        }
        #endregion



        #region TCP发送/获取数据
        public byte[] SendData(byte[] inBytes)
        {
            byte[] outBytes = null;
            int sendCount = 0;
            //通过clientSocket接收数据 
            Monitor.Enter(locked);
            try
            {
                //clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage));
                int len = inBytes.Length;
                byte[] sendBytes = new byte[len + 6];
                IntToBytes(len, 2).CopyTo(sendBytes, 4);
                inBytes.CopyTo(sendBytes, 6);
                clientSocket.Send(sendBytes);

                byte[] buffer = new byte[1024];
                len = clientSocket.Receive(buffer) - 6;

                if (len > 3)
                {
                    byte[] bytes1 = new byte[6];
                    IntToBytes(len, 2).CopyTo(bytes1, 4);

                    byte[] bytes2 = new byte[6];
                    Array.Copy(buffer, bytes2, 6);

                    if (CompareArray(bytes1, bytes2))
                    {
                        outBytes = new byte[len];
                        Array.Copy(buffer, 6, outBytes, 0, len);
                    }
                }

                //MessageBox.Show("接收服务器消息：{0}", Encoding.ASCII.GetString(buffer, 0, receiveLength));
            }
            catch
            {
                if (clientSocket == null || !clientSocket.Connected)
                {
                    ConnectToSever();
                    sendCount++;
                    if (sendCount < 3 && clientSocket.Connected) outBytes = SendData(inBytes);
                }
            }
            Monitor.Exit(locked);
            return outBytes;
        }
        #endregion



        #region 读线圈fun01 0X
        public bool[] fun01(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 1;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //读取数量

            byte[] ob = SendData(subuffer);

            if (ob != null)
            {
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (ob[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(ob[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }
            return outBools;
        }
        #endregion



        #region 读离散量fun02 1X
        public bool[] fun02(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 2;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //读取数量

            byte[] ob = SendData(subuffer);

            if (ob != null)
            {
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (ob[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(ob[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }

            return outBools;
        }
        #endregion



        #region 写单个线圈fun05 0X
        public bool fun05(byte Station, ushort inAddr, bool inBool)
        {
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;
            subuffer[1] = 5;
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);
            subuffer[4] = inBool ? (byte)0xff : (byte)0x00;
            subuffer[5] = 0x00;

            return CompareArray(subuffer, SendData(subuffer));
        }
        #endregion



        #region 写多个线圈fun0F 0X
        public bool fun0F(byte Station, ushort inAddr, bool[] inBools)
        {
            int len = inBools.Length;
            int byteNum = (len >> 3);
            if ((len & 0x07) > 0) byteNum++;

            byte[] sendBytes = new byte[byteNum];
            int bt = 0, j = 0;
            for (int i = 0; i < len; i++)
            {
                if (inBools[i]) bt |= (1 << j);
                j++;
                if (j > 7)
                {
                    sendBytes[i / 8] = (byte)bt;
                    j = 0;
                    bt = 0;
                }
            }
            if (j > 0) sendBytes[byteNum - 1] = (byte)bt;

            Byte[] subuffer = new Byte[byteNum + 7];
            subuffer[0] = Station;
            subuffer[1] = 0x0F;
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(len, 2).CopyTo(subuffer, 4);    //线圈数量
            subuffer[6] = (byte)byteNum;    //字节数
            Array.Copy(sendBytes, 0, subuffer, 7, byteNum);    //传送数据

            byte[] ob = SendData(subuffer);

            if (ob != null && ob.Length == 6)
            {
                byte[] send_data = new byte[6];
                Array.Copy(subuffer, 0, send_data, 0, 6);
                return CompareArray(send_data, ob);
            }
            else
            {
                return false;
            }
        }
        #endregion



        #region 读多个寄存器 fun03 4X
        public short[] fun03(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x03;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //数据个数

            byte[] ob = SendData(subuffer);

            if (ob != null && ob[2] == inNum * 2)
            {
                outShorts = new short[inNum];
                for (int i = 0; i < inNum; i++)
                {
                    outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                }
            }
            return outShorts;
        }
        #endregion



        #region 读多个输入寄存器 fun04 3X
        public short[] fun04(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x04;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //数据个数

            byte[] ob = SendData(subuffer);

            if (ob != null && ob[2] == inNum * 2)
            {
                outShorts = new short[inNum];
                for (int i = 0; i < inNum; i++)
                {
                    outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                }
            }
            return outShorts;
        }
        #endregion



        #region 写单个寄存器 fun06 4X
        public bool fun06(byte Station, ushort inAddr, short inShort)
        {
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x06;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inShort, 2).CopyTo(subuffer, 4);    //传送数据

            return CompareArray(subuffer, SendData(subuffer));
        }
        #endregion



        #region 写多个寄存器fun10 4X
        public bool fun10(byte Station, ushort inAddr, short[] inShorts)
        {
            int len = inShorts.Length;
            byte[] sendByte = new byte[len * 2];
            for (int i = 0; i < len; i++)
            {
                IntToBytes(inShorts[i], 2).CopyTo(sendByte, i * 2);
            }

            Byte[] subuffer = new Byte[len * 2 + 7];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x10;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(len, 2).CopyTo(subuffer, 4);    //数据个数
            subuffer[6] = (byte)(len * 2);   //字节长度
            sendByte.CopyTo(subuffer, 7);

            byte[] ob = SendData(subuffer);

            if (ob == null)
            {
                return false;
            }
            else
            {
                byte[] send_data = new byte[4];
                byte[] rec_data = new byte[4];
                Array.Copy(subuffer, 2, send_data, 0, 4);
                Array.Copy(ob, 2, rec_data, 0, 4);
                return CompareArray(send_data, rec_data);
            }
        }
        #endregion



        #region 判断两个数组是否相等
        public bool CompareArray(byte[] bt1, byte[] bt2)
        {
            if (bt1 == null && bt2 == null)
            {
                return true;
            }

            if (bt1 == null || bt2 == null)
            {
                return false;
            }

            int len = bt1.Length;
            if (len != bt2.Length)
            {
                return false;
            }
            for (var i = 0; i < len; i++)
            {
                if (bt1[i] != bt2[i])
                    return false;
            }
            return true;
        }
        #endregion



        #region 整形、字节、位互相转化

        //整形转字节数组
        private Byte[] IntToBytes(int inInt, int outNum)
        {
            Byte[] outByte = new byte[outNum];
            for (int i = 0; i < outNum; i++)
            {
                outByte[outNum - 1 - i] = (byte)(inInt & 0xFF);
                inInt >>= 8;
            }
            return outByte;
        }

        //字节数组转整形
        private int BytesToInt(byte[] inBytes)
        {
            int len = inBytes.Length;
            if (len > 4) len = 4;
            int outInt = 0;
            for (int i = 0; i < len; i++)
            {
                outInt = (outInt << 8) | inBytes[i];
            }
            return outInt;
        }

        //Bool数组转Byte
        private byte BoolsToByte(bool[] bools)
        {
            if (bools != null && bools.Length > 0)
            {
                byte b = 0;
                int len = bools.Length;
                if (len > 8) len = 8;
                for (int i = 0; i < len; i++)
                {
                    if (bools[i]) b = (Byte)(b | (1 << i));
                }
                return b;
            }
            return 0;
        }

        //Byte转Bool数组
        private bool[] ByteToBools(byte inByte)
        {
            Boolean[] array = new Boolean[8];
            for (int i = 0; i < 8; i++)
            { //对于byte的每bit进行判定  
                array[i] = (inByte & 1) == 1;   //判定byte的最后一位是否为1，若为1，则是true；否则是false  
                inByte = (byte)(inByte >> 1);       //将byte右移一位  
            }
            return array;
        }
        #endregion
    }

    /// <summary>
    /// Modbus Rtu 数据传输
    /// </summary>
    public class ModbusRtu
    {
        #region CRC查表
        private static readonly byte[] auchCRCHi = new byte[]//crc高位表
        {
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
            0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
            0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
            0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
            0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
        };

        private static readonly byte[] auchCRCLo = new byte[]//crc低位表
        {
            0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
            0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
            0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
            0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
            0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
            0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
            0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
            0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
            0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
            0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
            0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
            0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
            0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
            0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
            0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
            0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
            0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
            0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
            0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
            0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
            0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
            0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
            0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
            0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
            0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
            0x43, 0x83, 0x41, 0x81, 0x80, 0x40
        };
        #endregion

        private byte[] receiveBytes;
        private int indexCurr;
        private SerialPort m_sp;
        private int overTime;//接收超时
        object locked;

        #region ModBus类构造函数
        public ModbusRtu(string portName = "COM1", int baudRate = 9600, int dataBits = 8, int stopBits = 1, string parity = "None")    //（串口号"COM1"、波特率"9600"、数据位"7/8"、停止位"0/1"、校验方式"Odd/Even/None"）
        {
            m_sp = new SerialPort();
            m_sp.PortName = portName;
            m_sp.BaudRate = baudRate;
            m_sp.DataBits = dataBits;
            m_sp.StopBits = (System.IO.Ports.StopBits)stopBits;
            m_sp.Parity = (System.IO.Ports.Parity)Enum.Parse(typeof(Parity), parity, true);
            m_sp.ReadTimeout = -1;

            locked = new object();
            receiveBytes = new byte[1000];
            indexCurr = 0;
            overTime = 800;

            m_sp.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            open();
        }
        #endregion



        #region 打开串口
        public bool open()
        {
            if (!m_sp.IsOpen) m_sp.Open();
            Thread.Sleep(200);
            return m_sp.IsOpen;
        }
        #endregion



        #region 关闭串口
        public void close()
        {
            if (m_sp.IsOpen) m_sp.Close();
        }
        #endregion



        #region 接收返回数据
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            int len = m_sp.BytesToRead;
            if (len + indexCurr < receiveBytes.Length)
            {
                m_sp.Read(receiveBytes, indexCurr, len);
                indexCurr += len;
            }
            else
            {
                clear();
            }
        }

        private void clear()
        {
            Array.Clear(receiveBytes, 0, receiveBytes.Length);
            indexCurr = 0;
        }
        #endregion



        #region 计算法CRC16
        /*
        public static byte[] CRC16(byte[] data)
        {
            int len = data.Length;
            if (len > 0)
            {
                ushort crc = 0xFFFF;
                for (int i = 0; i < len; i++)
                {
                    crc = (ushort)(crc ^ (data[i]));
                    for (int j = 0; j < 8; j++)
                    {
                        crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                    }
                }
                byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
                byte lo = (byte)(crc & 0x00FF);         //低位置


                return new byte[] { hi, lo };
            }
            return new byte[] { 0, 0 };
        }
        */
        #endregion



        #region 查表法CRC16
        public byte[] CRC16(byte[] buffer)
        {
            byte crcHi = 0xff;  // 高位初始化

            byte crcLo = 0xff;  // 低位初始化

            for (int i = 0; i < buffer.Length; i++)
            {
                int crcIndex = crcHi ^ buffer[i];

                //查找crc表值
                crcHi = (byte)(crcLo ^ auchCRCHi[crcIndex]);
                crcLo = auchCRCLo[crcIndex];
            }
            return new byte[] { crcHi, crcLo };
        }
        #endregion



        #region 串口发送/获取返回数据
        public byte[] SendData(byte[] SendBuf)
        {
            byte[] outByte = null;
            if (m_sp.IsOpen)
            {
                Monitor.Enter(locked);

                int len = SendBuf.Length;
                byte[] sendBytes = new byte[len + 2];
                SendBuf.CopyTo(sendBytes, 0);
                CRC16(SendBuf).CopyTo(sendBytes, len);

                clear();
                m_sp.Write(sendBytes, 0, len + 2);

                int startTime = Environment.TickCount;
                Thread.Sleep(50);
                while (true)
                {
                    if (indexCurr > 0 && receiveBytes[0] != SendBuf[0]) break;

                    if (indexCurr > 1 && receiveBytes[1] != SendBuf[1]) break;

                    int receiveLen = 8;
                    if (SendBuf[1] == 0x01 || SendBuf[1] == 0x02)//0x01读多个线圈 0X; 0x02只读多个输入离散量 1X
                    {
                        receiveLen = (SendBuf[4] << 5) + (SendBuf[5] >> 3);
                        if ((SendBuf[5] & 0x07) > 0) receiveLen++;
                        receiveLen += 5;
                    }
                    else if (SendBuf[1] == 0x03 || SendBuf[1] == 0x04)//0x03读多个寄存器 4X; 0x04只读多个输入寄存器 3X
                    {
                        receiveLen = ((SendBuf[4] << 8) + SendBuf[5]) * 2 + 5;
                    }

                    if (indexCurr >= receiveLen)
                    {
                        byte[] rec = new byte[receiveLen - 2];
                        Array.Copy(receiveBytes, 0, rec, 0, rec.Length);
                        byte[] crc = CRC16(rec);
                        if (crc[0] == receiveBytes[receiveLen - 2] && crc[1] == receiveBytes[receiveLen - 1])
                        {
                            outByte = rec;
                        }
                        break;
                    }

                    int endTime = Environment.TickCount;
                    if (startTime > endTime || (endTime - startTime) > overTime) break;
                }
                Monitor.Exit(locked);
            }

            return outByte;
        }
        #endregion



        #region 读线圈fun01 0X
        public bool[] fun01(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 1;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //读取数量

            byte[] ob = SendData(subuffer);

            if (ob != null)
            {
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (ob[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(ob[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }
            return outBools;
        }
        #endregion



        #region 读离散量fun02 1X
        public bool[] fun02(byte Station, ushort inAddr, ushort inNum)
        {
            bool[] outBools = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 2;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //读取数量

            byte[] ob = SendData(subuffer);

            if (ob != null)
            {
                int inN = inNum >> 3;
                if ((inNum & 0x07) > 0) inN++;
                if (ob[2] == inN)
                {
                    bool[] recBool = new bool[inN * 8];
                    for (int i = 0; i < inN; i++)
                    {
                        ByteToBools(ob[i + 3]).CopyTo(recBool, i * 8);
                    }
                    outBools = new bool[inNum];
                    Array.Copy(recBool, 0, outBools, 0, inNum);
                }
            }

            return outBools;
        }
        #endregion



        #region 写单个线圈fun05 0X
        public bool fun05(byte Station, ushort inAddr, bool inBool)
        {
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;
            subuffer[1] = 5;
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);
            subuffer[4] = inBool ? (byte)0xff : (byte)0x00;
            subuffer[5] = 0x00;

            return CompareArray(subuffer, SendData(subuffer));
        }
        #endregion



        #region 写多个线圈fun0F 0X
        public bool fun0F(byte Station, ushort inAddr, bool[] inBools)
        {
            int len = inBools.Length;
            int byteNum = (len >> 3);
            if ((len & 0x07) > 0) byteNum++;

            byte[] sendBytes = new byte[byteNum];
            int bt = 0, j = 0;
            for (int i = 0; i < len; i++)
            {
                if (inBools[i]) bt |= (1 << j);
                j++;
                if (j > 7)
                {
                    sendBytes[i / 8] = (byte)bt;
                    j = 0;
                    bt = 0;
                }
            }
            if (j > 0) sendBytes[byteNum - 1] = (byte)bt;

            Byte[] subuffer = new Byte[byteNum + 7];
            subuffer[0] = Station;
            subuffer[1] = 0x0F;
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(len, 2).CopyTo(subuffer, 4);    //线圈数量
            subuffer[6] = (byte)byteNum;    //字节数
            Array.Copy(sendBytes, 0, subuffer, 7, byteNum);    //传送数据

            byte[] ob = SendData(subuffer);

            if (ob != null && ob.Length == 6)
            {
                byte[] send_data = new byte[6];
                Array.Copy(subuffer, 0, send_data, 0, 6);
                return CompareArray(send_data, ob);
            }
            else
            {
                return false;
            }
        }
        #endregion



        #region 读多个寄存器 fun03 4X
        public short[] fun03(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x03;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //数据个数

            byte[] ob = SendData(subuffer);

            if (ob != null && ob[2] == inNum * 2)
            {
                outShorts = new short[inNum];
                for (int i = 0; i < inNum; i++)
                {
                    outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                }
            }
            return outShorts;
        }
        #endregion



        #region 读多个输入寄存器 fun04 3X
        public short[] fun04(byte Station, ushort inAddr, ushort inNum)
        {
            short[] outShorts = null;
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x04;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inNum, 2).CopyTo(subuffer, 4);    //数据个数

            byte[] ob = SendData(subuffer);

            if (ob != null && ob[2] == inNum * 2)
            {
                outShorts = new short[inNum];
                for (int i = 0; i < inNum; i++)
                {
                    outShorts[i] = (short)((ob[2 * i + 3] << 8) + ob[2 * i + 4]);
                }
            }
            return outShorts;
        }
        #endregion



        #region 写单个寄存器 fun06 4X
        public bool fun06(byte Station, ushort inAddr, short inShort)
        {
            Byte[] subuffer = new Byte[6];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x06;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(inShort, 2).CopyTo(subuffer, 4);    //传送数据

            return CompareArray(subuffer, SendData(subuffer));
        }
        #endregion



        #region 写多个寄存器fun10 4X
        public bool fun10(byte Station, ushort inAddr, short[] inShorts)
        {
            int len = inShorts.Length;
            byte[] sendByte = new byte[len * 2];
            for (int i = 0; i < len; i++)
            {
                IntToBytes(inShorts[i], 2).CopyTo(sendByte, i * 2);
            }

            Byte[] subuffer = new Byte[len * 2 + 7];
            subuffer[0] = Station;    //站号
            subuffer[1] = 0x10;    //功能码
            IntToBytes(inAddr, 2).CopyTo(subuffer, 2);    //起始地址
            IntToBytes(len, 2).CopyTo(subuffer, 4);    //数据个数
            subuffer[6] = (byte)(len * 2);   //字节长度
            sendByte.CopyTo(subuffer, 7);

            byte[] ob = SendData(subuffer);

            if (ob == null)
            {
                return false;
            }
            else
            {
                byte[] send_data = new byte[4];
                byte[] rec_data = new byte[4];
                Array.Copy(subuffer, 2, send_data, 0, 4);
                Array.Copy(ob, 2, rec_data, 0, 4);
                return CompareArray(send_data, rec_data);
            }
        }
        #endregion



        #region 判断两个数组是否相等
        public bool CompareArray(byte[] bt1, byte[] bt2)
        {
            if (bt1 == null && bt2 == null)
            {
                return true;
            }

            if (bt1 == null || bt2 == null)
            {
                return false;
            }

            int len = bt1.Length;
            if (len != bt2.Length)
            {
                return false;
            }
            for (var i = 0; i < len; i++)
            {
                if (bt1[i] != bt2[i])
                    return false;
            }
            return true;
        }
        #endregion



        #region 整形、字节、位互相转化

        //整形转字节数组
        private Byte[] IntToBytes(int inInt, int outNum)
        {
            Byte[] outByte = new byte[outNum];
            for (int i = 0; i < outNum; i++)
            {
                outByte[outNum - 1 - i] = (byte)(inInt & 0xFF);
                inInt >>= 8;
            }
            return outByte;
        }

        //字节数组转整形
        private int BytesToInt(byte[] inBytes)
        {
            int len = inBytes.Length;
            if (len > 4) len = 4;
            int outInt = 0;
            for (int i = 0; i < len; i++)
            {
                outInt = (outInt << 8) | inBytes[i];
            }
            return outInt;
        }

        //Bool数组转Byte
        private byte BoolsToByte(bool[] bools)
        {
            if (bools != null && bools.Length > 0)
            {
                byte b = 0;
                int len = bools.Length;
                if (len > 8) len = 8;
                for (int i = 0; i < len; i++)
                {
                    if (bools[i]) b = (Byte)(b | (1 << i));
                }
                return b;
            }
            return 0;
        }

        //Byte转Bool数组
        private bool[] ByteToBools(byte inByte)
        {
            Boolean[] array = new Boolean[8];
            for (int i = 0; i < 8; i++)
            { //对于byte的每bit进行判定  
                array[i] = (inByte & 1) == 1;   //判定byte的最后一位是否为1，若为1，则是true；否则是false  
                inByte = (byte)(inByte >> 1);       //将byte右移一位  
            }
            return array;
        }
        #endregion
    }



}
